package com.glsc.ngateway.flowable.service.process;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.glsc.ngateway.common.api.common.dto.PageDataResult;
import com.glsc.ngateway.common.api.flowable.dto.CurrentTaskInfoDto;
import com.glsc.ngateway.common.api.flowable.dto.param.*;
import com.glsc.ngateway.common.api.flowable.resp.*;
import com.glsc.ngateway.common.base.domain.mysql.flowable.FlowableCommentSupplement;
import com.glsc.ngateway.common.base.domain.mysql.flowable.FlowableDetail;
import com.glsc.ngateway.flowable.common.FullComment;
import com.glsc.ngateway.flowable.common.TaskComment;
import com.glsc.ngateway.flowable.constant.FlowableConstant;
import com.glsc.ngateway.flowable.service.FlowableCommonService;
import com.glsc.ngateway.flowable.service.FlowableCopyService;
import com.glsc.ngateway.flowable.service.FlowableDetailService;
import com.glsc.ngateway.flowable.service.FlowableEhrEmpService;
import com.glsc.ngateway.flowable.service.feign.IEurekaAmproductFeignService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.util.Strings;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.TaskQueryProperty;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

@Service
public class ProcessService {

    private static Logger logger = LoggerFactory.getLogger(ProcessService.class);
    @Autowired
    FlowableCommonService flowableCommonService;
    @Autowired
    FlowableDetailService flowableDetailService;
    @Autowired
    FlowableCopyService flowableCopyService;
    @Autowired
    FlowableEhrEmpService flowableEhrEmpService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    IEurekaAmproductFeignService eurekaAmproductFeignService;
    @Resource
    private FlowableCommentSupplementService flowableCommentSupplementService;
    @Resource
    @Qualifier("namedParameterJdbcTemplate")
    private NamedParameterJdbcTemplate namedParameterJdbcTemplateMysql;
    @Resource
    private CaffeineCacheManager cacheManager;

    public PageDataResult<ResponseSituationDto> searchDealProcessListOld(Pageable pageable, ParamSearchDto params) {
        List<ResponseSituationDto> list = new ArrayList<>();
        String title = params.getTitle();
        List<String> dealUser = Strings.isNotBlank(params.getDealUser()) ? Collections.singletonList(params.getDealUser()) : new ArrayList<>();
        if (CollectionUtil.isNotEmpty(params.getDealUserList())) {
            dealUser.addAll(params.getDealUserList());
            dealUser = dealUser.stream().distinct().collect(Collectors.toList());
        }
        String processDefineKeyOrig = params.getProcessDefineKey();
        String sysid = params.getSysid();
        List<String> processDefineKeyList = StrUtil.isNotEmpty(processDefineKeyOrig) ? StrUtil.split(processDefineKeyOrig, ',') : new ArrayList<>();
        if (CollectionUtil.isNotEmpty(params.getProcessDefineKeyList())) {
            processDefineKeyList.addAll(params.getProcessDefineKeyList());
            processDefineKeyList = processDefineKeyList.stream().distinct().collect(Collectors.toList());
        }
        long totalRowCnt;
        int pageRowStartNo = ((Long) pageable.getOffset()).intValue();
        List<Task> taskPageList;
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (StrUtil.isNotEmpty(sysid)) {
            taskQuery.taskTenantId(sysid);
        }
        if (StrUtil.isNotEmpty(title)) {
            taskQuery.processInstanceBusinessKeyLike("%" + title + "%");
        }
        if (CollectionUtil.isNotEmpty(processDefineKeyList)) {
            taskQuery.processDefinitionKeyIn(processDefineKeyList);
        }
        if (CollectionUtil.isNotEmpty(dealUser)) {
            taskQuery.taskAssigneeIds(dealUser);
        }
        if (CollectionUtil.isNotEmpty(params.getProcessIdList())) {
            taskQuery.processInstanceIdIn(params.getProcessIdList());
        }
        taskPageList = taskQuery.orderBy(TaskQueryProperty.CREATE_TIME).desc().listPage(pageRowStartNo, pageable.getPageSize());
        totalRowCnt = taskQuery.count();
        Map<String, String> taskStatusMap = getTaskHandleMapByTaskList(taskPageList);

        for (Task task : taskPageList) {
            String processId = task.getProcessInstanceId();
            if (processId == null) {
                //说明是子任务，没有processid
                Task parentTask = taskService.createTaskQuery().taskId(task.getParentTaskId()).singleResult();
                processId = parentTask.getProcessInstanceId();
            }
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            ResponseSituationDto psd = new ResponseSituationDto();
            psd.setCurrentTaskId(task.getId());
            psd.setCurrentTaskName(task.getName());
            psd.setCurrentTaskDefinitionKey(task.getTaskDefinitionKey());
            psd.setProcessTitle(pi.getName());
            psd.setProcessId(pi.getId());
            psd.setProcessDefineName(pi.getProcessDefinitionName());
            psd.setProcessDefineKey(pi.getProcessDefinitionKey());
            psd.setStartTime(convertTime(pi.getStartTime()));
            psd.setStartUsername(pi.getStartUserId());
            psd.setStartUserChineseName(flowableEhrEmpService.getChineseName(pi.getStartUserId()));
            psd.setCurrentAssignee(task.getAssignee());
            psd.setCurrentAssigneeName(flowableEhrEmpService.getChineseName(task.getAssignee()));
            psd.setSysid(pi.getTenantId());
            psd.setStatus("0");
            psd.setCurrentTaskHandleStatus(taskStatusMap.get(task.getId()));

            if (StrUtil.equals(pi.getProcessDefinitionKey(), FlowableConstant.AM_PROD_FORWARD_FLOW)) {
                Object exProcessId = historyService.createHistoricVariableInstanceQuery().processInstanceId(pi.getProcessInstanceId()).variableName("exProcessId").singleResult().getValue();
                HistoricProcessInstance expi = historyService.createHistoricProcessInstanceQuery().processInstanceId(StrUtil.toString(exProcessId)).singleResult();
                if (expi != null) {
                    psd.setProcessId(expi.getId());
                    psd.setProcessDefineKey(expi.getProcessDefinitionKey());
                }
            }
            list.add(psd);
        }
        PageDataResult<ResponseSituationDto> pageDataResult = new PageDataResult<>();
        pageDataResult.setList(list);
        pageDataResult.setTotals((int) totalRowCnt);
        return pageDataResult;
    }

    private Map<String, String> getTaskHandleMapByTaskList(List<Task> taskPageList) {
        List<String> collect = taskPageList.stream().map(Task::getId).collect(Collectors.toList());
        List<FlowableCommentSupplement> fcList = flowableCommentSupplementService.findByTaskIdList(collect);
        return fcList.stream().collect(Collectors.toMap(
                FlowableCommentSupplement::getTaskId,
                FlowableCommentSupplement::getTaskHandleStatus,
                (existingValue, newValue) -> existingValue));
    }


    public PageDataResult<ResponseSituationDto> searchInvolveUserProcessList(Pageable pageable, ParamSearchDto params) throws ParseException {
        List<ResponseSituationDto> list = new ArrayList<>();
        String title = params.getTitle();
        String processDefineKeyOrig = params.getProcessDefineKey();
        String sysid = params.getSysid();
        String involveUser = params.getInvolveUser();
        String endStr = params.getHasProcessEnd();
        List<String> processDefineKeyList = StrUtil.isNotEmpty(processDefineKeyOrig) ? StrUtil.split(processDefineKeyOrig, ',') : new ArrayList<>();
        Date begin = StrUtil.isNotBlank(params.getBeginDate()) ? DateUtil.parse(params.getBeginDate(), "yyyyMMdd").toJdkDate() : null;
        Date end = StrUtil.isNotBlank(params.getEndDate()) ? DateUtil.offsetDay(DateUtil.parse(params.getEndDate(), "yyyyMMdd"), 1).toJdkDate() : null;
        long totalRowCnt;
        List<HistoricProcessInstance> hpis = new ArrayList<>();
        title = StrUtil.isEmpty(title) ? null : title;
        sysid = StrUtil.isEmpty(sysid) ? null : sysid;
        String hasEndForInvolve = null;
        if (StrUtil.isNotEmpty(endStr)) {
            hasEndForInvolve = StrUtil.equals(endStr, "1") ? "1" : "0";
        }
        List<String> processIdNotList = Objects.nonNull(params.getArchived()) && params.getArchived() ? flowableDetailService.findAllByStatus("2").stream().map(FlowableDetail::getProcessInstanceId).collect(Collectors.toList()) : null;
        Page<List<Map<String, String>>> done = flowableCommonService.findUserDone(involveUser, processDefineKeyOrig, processDefineKeyList, title, begin, end, sysid, hasEndForInvolve, params.getProcessIdList(), processIdNotList, pageable);
        logger.info("searchInvolveUserProcessList done size :" + done.stream().count());
        for (Object row : done.getContent()) {
            Map<String, Object> rowColMap = (Map<String, Object>) row;
            HistoricProcessInstanceEntityImpl hp = new HistoricProcessInstanceEntityImpl();
            hp.setName(rowColMap.get("title").toString());
//            String defineName = "";
//            if (rowColMap.get("processDefinitionId").toString().contains(":")) {
//                ProcessDefinition ss = repositoryService.createProcessDefinitionQuery().processDefinitionKey(rowColMap.get("processDefinitionId").toString().split(":")[0]).orderByProcessDefinitionVersion().desc().list().get(0);
//                defineName = ss.getName();
//            } else if (rowColMap.get("businessKey") != null) {
//                ProcessDefinition ss = repositoryService.createProcessDefinitionQuery().processDefinitionKey(rowColMap.get("businessKey").toString().split("\\.")[0]).orderByProcessDefinitionVersion().desc().list().get(0);
//                defineName = ss.getName();
//            }
            hp.setProcessDefinitionName(rowColMap.get("processDefinitionName").toString());
            hp.setStartUserId(rowColMap.get("startUserId").toString());
            hp.setTenantId(rowColMap.get("tenantId").toString());
            hp.setStartTime((Date) rowColMap.get("startTime"));
            hp.setProcessInstanceId(rowColMap.get("processInstanceId").toString());
            hp.setId(rowColMap.get("processInstanceId").toString());
            hpis.add(hp);
        }
        totalRowCnt = done.getTotalElements();
        buildResSituationDtoOld(sysid, list, processDefineKeyList, hpis);
        PageDataResult<ResponseSituationDto> pageDataResult = new PageDataResult<>();
        pageDataResult.setList(list);
        pageDataResult.setTotals((int) totalRowCnt);
        return pageDataResult;
    }

    public PageDataResult<ResponseSituationDto> searchProcessListOld(Pageable pageable, ParamSearchDto params) {
        List<ResponseSituationDto> list = new ArrayList<>();
        String title = params.getTitle();
        String startUser = params.getStartUser();
        String sysid = params.getSysid();
        String endStr = params.getHasProcessEnd();
        List<String> processDefineKeyList = StrUtil.isNotEmpty(params.getProcessDefineKey()) ? StrUtil.split(params.getProcessDefineKey(), ',') : new ArrayList<>();
        Date begin = StrUtil.isNotBlank(params.getBeginDate()) ? DateUtil.parse(params.getBeginDate(), "yyyyMMdd").toJdkDate() : null;
        Date end = StrUtil.isNotBlank(params.getEndDate()) ? DateUtil.offsetDay(DateUtil.parse(params.getEndDate(), "yyyyMMdd"), 1).toJdkDate() : null;
        long totalRowCnt;
        int pageRowStartNo = ((Long) pageable.getOffset()).intValue();
        List<HistoricProcessInstance> hpis;
        title = StrUtil.isEmpty(title) ? null : title;
        sysid = StrUtil.isEmpty(sysid) ? null : sysid;
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery().startedBefore(end).startedAfter(begin);
        if (StrUtil.isNotEmpty(endStr)) {
            if (StrUtil.equals(endStr, "1")) {
                historicProcessInstanceQuery.finished();
            } else if (StrUtil.equals(endStr, "0")) {
                historicProcessInstanceQuery.unfinished();
            }
        }
        if (StrUtil.isNotEmpty(sysid)) {
            historicProcessInstanceQuery.processInstanceTenantId(sysid);
        }
        if (StrUtil.isNotEmpty(title)) {
            historicProcessInstanceQuery.processInstanceNameLike("%" + title + "%");
        }
        if (CollectionUtil.isNotEmpty(processDefineKeyList)) {
            historicProcessInstanceQuery.processDefinitionKeyIn(processDefineKeyList);
        }
        if (StrUtil.isNotBlank(startUser)) {
            historicProcessInstanceQuery.startedBy(startUser);
        } else {
            historicProcessInstanceQuery.processDefinitionKeyNotIn(List.of(FlowableConstant.AM_PROD_FORWARD_FLOW));
        }
        if (CollectionUtil.isNotEmpty(params.getProcessIdList())) {
            historicProcessInstanceQuery.processInstanceIds(new HashSet<>(params.getProcessIdList()));
        }
        hpis = historicProcessInstanceQuery.orderByProcessInstanceStartTime().desc().listPage(pageRowStartNo, pageable.getPageSize());
        totalRowCnt = historicProcessInstanceQuery.count();

        buildResSituationDtoOld(sysid, list, processDefineKeyList, hpis);
        PageDataResult<ResponseSituationDto> pageDataResult = new PageDataResult<>();
        pageDataResult.setList(list);
        pageDataResult.setTotals((int) totalRowCnt);
        return pageDataResult;
    }

    private void buildResSituationDtoOld(String sysid, List<ResponseSituationDto> list, List<String> processDefineKeyList, List<HistoricProcessInstance> hpis) {
        for (HistoricProcessInstance pi : hpis) {
            ResponseSituationDto psd = new ResponseSituationDto();
            List<CurrentTaskInfoDto> currentTaskInfoDtoList = new ArrayList<>();
            psd.setStatus("0");
            TaskQuery taskQuery = taskService.createTaskQuery();
            if (StrUtil.isNotEmpty(sysid)) {
                taskQuery.taskTenantId(sysid);
            }
            if (CollectionUtil.isNotEmpty(processDefineKeyList)) {
                taskQuery.processDefinitionKeyIn(processDefineKeyList);
            }
            if (StrUtil.isNotEmpty(pi.getId())) {
                taskQuery.processInstanceId(pi.getId());
            }
            List<Task> taskList = taskQuery.orderByTaskCreateTime().desc().list();
            Map<String, String> taskStatusMap = getTaskHandleMapByTaskList(taskList);
            //如果流程没结束，查询到任务节点
            if (!checkProcessEnd(pi.getId())) {
                if (CollectionUtil.isNotEmpty(taskList)) {
                    Map<String, List<String>> assigneemap = new HashMap<>();
                    Map<String, List<String>> taskidmap = new HashMap<>();
                    Map<String, List<String>> datemap = new HashMap<>();
                    Map<String, List<String>> statusmap = new HashMap<>();
                    List<String> sbkeyList = new ArrayList<>();
                    List<String> sbnameList = new ArrayList<>();
                    List<String> sbidList = new ArrayList<>();
                    List<String> startTimeList = new ArrayList<>();
                    List<String> sbassigneeList = new ArrayList<>();
                    List<String> sbStatusList = new ArrayList<>();
                    taskList.forEach(task -> {
                        sbkeyList.add(task.getTaskDefinitionKey());
                        sbnameList.add(task.getName());
                        sbidList.add(task.getId());
                        startTimeList.add(DateUtil.format(task.getCreateTime(), "yyyy-MM-dd hh:mm:ss"));
                        sbassigneeList.add(task.getAssignee());
                        sbStatusList.add(taskStatusMap.get(task.getId()));

                        String key = task.getTaskDefinitionKey() + "|" + task.getName();
                        List<String> assigneeList = new ArrayList<>();
                        List<String> taskIdList = new ArrayList<>();
                        List<String> dateList = new ArrayList<>();
                        List<String> statusList = new ArrayList<>();

                        if (assigneemap.get(key) != null) {
                            assigneeList = assigneemap.get(key);
                        }
                        assigneeList.add(task.getAssignee());
                        assigneemap.put(key, assigneeList);

                        if (taskidmap.get(key) != null) {
                            taskIdList = taskidmap.get(key);
                        }
                        taskIdList.add(task.getId());
                        taskidmap.put(key, taskIdList);

                        if (datemap.get(key) != null) {
                            dateList = datemap.get(key);
                        }
                        dateList.add(convertTime(task.getCreateTime()));
                        datemap.put(key, dateList);

                        if (statusmap.get(key) != null) {
                            statusList = statusmap.get(key);
                        }
                        statusList.add(taskStatusMap.get(task.getId()));
                        statusmap.put(key, statusList);
                    });

                    psd.setCurrentTaskDefinitionKey(CollectionUtil.join(sbkeyList, "|"));
                    psd.setCurrentTaskName(CollectionUtil.join(sbnameList, "|"));
                    psd.setCurrentTaskId(CollectionUtil.join(sbidList, "|"));
                    psd.setCurrentAssignee(CollectionUtil.join(sbassigneeList, "|"));
                    psd.setCurrentTaskStartTime(CollectionUtil.join(startTimeList, "|"));
                    psd.setCurrentAssigneeName(flowableEhrEmpService.getChineseNameAll(sbassigneeList));
                    psd.setCurrentTaskHandleStatus(CollectionUtil.join(sbStatusList, "|"));

                    for (Map.Entry<String, List<String>> m : assigneemap.entrySet()) {
                        List<String> userid = m.getValue();
                        List<String> username = new ArrayList<>();
                        for (String id : userid) {
                            username.add(flowableEhrEmpService.getChineseName(id));
                        }
                        String[] defName = m.getKey().split("\\|");
                        CurrentTaskInfoDto currentTaskInfoDto = new CurrentTaskInfoDto();
                        currentTaskInfoDto.setCurrentTaskName(defName[1]);
                        currentTaskInfoDto.setCurrentTaskDefinitionKey(defName[0]);
                        currentTaskInfoDto.setCurrentAssigneeList(m.getValue());
                        currentTaskInfoDto.setCurrentAssigneeNameList(username);
                        currentTaskInfoDto.setCurrentTaskIdList(taskidmap.get(m.getKey()));
                        currentTaskInfoDto.setCurrentTaskCreateTimeList(datemap.get(m.getKey()));
                        currentTaskInfoDto.setCurrentTaskStatusList(statusmap.get(m.getKey()));
                        currentTaskInfoDtoList.add(currentTaskInfoDto);
                    }
                }
            } else {
                psd.setStatus("1");
                List<FlowableDetail> delist = flowableDetailService.findDetailByProcessId(pi.getId());
                if (delist != null && delist.size() > 0) {
                    String status = delist.get(0).getProcessStatus();
                    //status2为stop手动结束，置状态
                    //status3为delete，不会出现在这个接口
                    if (("2").equals(status)) {
                        psd.setStatus("2");
                    }
                }
            }
            psd.setProcessTitle(pi.getName());
            psd.setProcessId(pi.getId());
            psd.setProcessDefineName(pi.getProcessDefinitionName());
            ProcessInstance p = runtimeService.createProcessInstanceQuery().processInstanceId(pi.getId()).singleResult();
            if (p == null) {//pi.getProcessDefinitionKey() 历史数据中有可能不存在
                HistoricProcessInstance hp = historyService.createHistoricProcessInstanceQuery().processInstanceId(pi.getId()).singleResult();
                if (hp != null) {
                    psd.setProcessDefineKey(hp.getProcessDefinitionKey());
                }
            } else {
                psd.setProcessDefineKey(p.getProcessDefinitionKey());
            }
            psd.setStartTime(convertTime(pi.getStartTime()));
            if (pi.getEndTime() != null) {//如果流程结束，则返回流程结束时间
                psd.setEndTime(convertTime(pi.getEndTime()));
            }
            psd.setStartUsername(pi.getStartUserId());
            psd.setStartUserChineseName(flowableEhrEmpService.getChineseName(pi.getStartUserId()));
            psd.setSysid(pi.getTenantId());
            psd.setCurrentTaskInfoDtoList(currentTaskInfoDtoList);
            if (CollUtil.isNotEmpty(currentTaskInfoDtoList)) {
                List<String> collect = currentTaskInfoDtoList.stream().map(dto -> dto.getCurrentTaskName() + "(" + CollUtil.join(dto.getCurrentAssigneeNameList(), ",") + ")").collect(Collectors.toList());
                psd.setCurrentTaskSummaryInfo(CollUtil.join(collect, ","));
            }

            if (StrUtil.equals(psd.getProcessDefineKey(), FlowableConstant.AM_PROD_FORWARD_FLOW)) {
                HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(((HistoricProcessInstanceEntityImpl) pi).getProcessInstanceId()).list().stream().filter(e -> e.getVariableName().equals("exProcessId")).findFirst().get();
                HistoricProcessInstance expi = historyService.createHistoricProcessInstanceQuery().processInstanceId(StrUtil.toString(variableInstance.getValue())).singleResult();
                if (expi != null) {
                    psd.setProcessId(expi.getId());
                    psd.setProcessDefineKey(expi.getProcessDefinitionKey());
                }
            }
            list.add(psd);
        }
    }

    public List<ParticipateRespDto> searchUserParticipateProcessList(ParticipateParamDto paramDto) {
        List<ParticipateRespDto> respList = new ArrayList<>();
        if (StrUtil.isBlank(paramDto.getParticipateUser()) || StrUtil.isBlank(paramDto.getSysId()) || CollectionUtil.isEmpty(paramDto.getProcessIdList())) {
            return respList;
        }
        List<Map<String, String>> hisProcessList = flowableCommonService.findUserParticipateProcessList(paramDto.getProcessIdList(), paramDto.getParticipateUser(), paramDto.getSysId());
        logger.info("已办+待办 流程列表:{}", hisProcessList);
        if (CollectionUtil.isNotEmpty(hisProcessList)) {
            respList.addAll(hisProcessList.stream().map(map -> BeanUtil.mapToBean(map, ParticipateRespDto.class, true)).collect(Collectors.toList()));
        }
        List<Map<String, String>> copyeProcessList = flowableCopyService.getUserParticipateProcessList(paramDto.getProcessIdList(), paramDto.getParticipateUser(), paramDto.getSysId());
        logger.info("抄送 流程列表:{}", hisProcessList);
        if (CollectionUtil.isNotEmpty(copyeProcessList)) {
            respList.addAll(copyeProcessList.stream().map(map -> BeanUtil.mapToBean(map, ParticipateRespDto.class, true)).collect(Collectors.toList()));
        }
        return respList;
    }

    public boolean checkProcessEnd(@RequestParam("processId") String processId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
        if (Objects.isNull(historicProcessInstance)) {
            throw new RuntimeException("流程不存在:" + processId);
        }
        return ObjectUtil.isNotNull(historicProcessInstance.getEndTime());
    }


    private String convertTime(Date time) {
        return DateUtil.format(time, "yyyy-MM-dd HH:mm:ss");
    }


    public ResponseSituationDto postCreateCallActivityProcess(String processId, String sysid, String userid) {
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().superProcessInstanceId(processId).active().orderByStartTime().desc().list();
        String taskId = "";
        if (CollectionUtil.isNotEmpty(list)) {
            ProcessInstance processInstance = list.get(0);
            Execution execution = runtimeService.createExecutionQuery().processInstanceId(processInstance.getProcessInstanceId()).onlyChildExecutions().singleResult();
            if (execution.getActivityId().equals("beginTask")) {
                String newtitle = processInstance.getName();
                String businesskey = processInstance.getProcessDefinitionKey() + "." + newtitle;
                runtimeService.updateBusinessKey(processInstance.getProcessInstanceId(), businesskey);
                runtimeService.setProcessInstanceName(processInstance.getProcessInstanceId(), newtitle);
                runtimeService.setVariable(execution.getId(), "_FLOWABLE_SKIP_EXPRESSION_ENABLED", true);
//                Map<String, Object> variables = runtimeService.getVariables(execution.getId());
                Task task = taskService.createTaskQuery().executionId(execution.getId()).singleResult();
                taskId = task.getId();
//                String createComment = StrUtil.toString(variables.get("o32createComment"));
//                taskService.addComment(task.getId(), task.getProcessInstanceId(), createComment);
                //信息入库
                flowableDetailService.addProcessInstanceByCreate(processInstance, sysid, "", userid, newtitle);
                //入库
                flowableDetailService.addProcessInstanceByTask(task, processInstance, sysid, "", newtitle, "create");
            }
        }

        return this.getTaskInfo(taskId, sysid);
    }

    public ResponseSituationDto getTaskInfo(String taskId, String sysid) {

        ResponseSituationDto psd = new ResponseSituationDto();
        List<Task> tasklist = taskService.createTaskQuery().taskId(taskId).taskTenantId(sysid).list();
        if (tasklist == null || tasklist.size() == 0) {
            return psd;
        }
        Task task = tasklist.get(0);
        List<ProcessInstance> pilist = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).processInstanceTenantId(sysid).list();
        if (pilist == null || pilist.size() == 0) {
            return psd;
        }
        ProcessInstance pi = pilist.get(0);

        psd.setCurrentTaskId(task.getId());
        psd.setCurrentTaskName(task.getName());
        psd.setCurrentTaskDefinitionKey(task.getTaskDefinitionKey());
        psd.setProcessTitle(pi.getName());
        psd.setProcessId(pi.getId());
        psd.setProcessDefineName(pi.getProcessDefinitionName());
        psd.setProcessDefineKey(pi.getProcessDefinitionKey());
        psd.setStartTime(convertTime(pi.getStartTime()));
        psd.setStartUsername(pi.getStartUserId());
        psd.setStartUserChineseName(flowableEhrEmpService.getChineseName(pi.getStartUserId()));
        psd.setCurrentAssignee(task.getAssignee());
        psd.setCurrentAssigneeName(flowableEhrEmpService.getChineseName(task.getAssignee()));
        psd.setSysid(pi.getTenantId());
        psd.setStatus("0");
        if (StrUtil.equals(pi.getProcessDefinitionKey(), FlowableConstant.AM_PROD_FORWARD_FLOW)) {
            Object exProcessId = historyService.createHistoricVariableInstanceQuery().processInstanceId(pi.getProcessInstanceId()).variableName("exProcessId").singleResult().getValue();
            HistoricProcessInstance expi = historyService.createHistoricProcessInstanceQuery().processInstanceId(StrUtil.toString(exProcessId)).singleResult();
            if (expi != null) {
                psd.setProcessId(expi.getId());
                psd.setProcessDefineKey(expi.getProcessDefinitionKey());
            }
        }
        return psd;
    }

    public ResponseSituationDto getHisTaskInfo(String taskId, String sysid) {
        ResponseSituationDto psd = new ResponseSituationDto();
        List<HistoricTaskInstance> taskList = historyService.createHistoricTaskInstanceQuery().taskId(taskId).taskTenantId(sysid).list();
        if (CollUtil.isEmpty(taskList)) {
            return psd;
        }
        HistoricTaskInstance task = taskList.get(0);
        List<ProcessInstance> pilist = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).processInstanceTenantId(sysid).list();
        if (CollUtil.isEmpty(pilist)) {
            return psd;
        }
        ProcessInstance pi = pilist.get(0);

        psd.setCurrentTaskId(task.getId());
        psd.setCurrentTaskName(task.getName());
        psd.setCurrentTaskDefinitionKey(task.getTaskDefinitionKey());
        psd.setProcessTitle(pi.getName());
        psd.setProcessId(pi.getId());
        psd.setCurrentAssignee(task.getAssignee());
        psd.setCurrentAssigneeName(flowableEhrEmpService.getChineseName(task.getAssignee()));
        psd.setSysid(pi.getTenantId());
        return psd;
    }

    public void resignatedEmpChangeAssignee(String oldAccount, String newAccount, String sysId) {
        List<Task> taskList = taskService.createTaskQuery().taskTenantId(sysId).taskAssignee(oldAccount).active().list();
        // 当前任务待办,可直接变更会签人
        if (CollectionUtil.isNotEmpty(taskList)) {
            taskList.forEach(task -> {
                taskService.setAssignee(task.getId(), newAccount);
                logger.info("任务变更!!!原会签人:" + oldAccount + ",新会签人:" + newAccount + ";任务:" + task.getName() + "/" + task.getId());
            });
        }
        //进行中的任务,需将会签人变量进行变更,并按需修改相应的size属性
        List<ProcessInstance> processList = runtimeService.createProcessInstanceQuery().processInstanceTenantId(sysId).active().list();
        if (CollectionUtil.isNotEmpty(processList)) {
            //遍历所有进行中的流程
            processList.forEach(process -> {
                Map<String, Object> variables = runtimeService.getVariables(process.getId());
                //获取流程变量,判断是否需要变量修改
                variables.entrySet().forEach(entry -> {
                    //暂定只改***Group 数据
                    if (entry.getKey().endsWith("Group")) {
                        //***Group变量的值可能为多种数据类型,需要区分处理
                        if (entry.getValue() instanceof Set) {
                            if (((Set<String>) entry.getValue()).contains(oldAccount)) {
                                ((Set<String>) entry.getValue()).remove(oldAccount);
                                //判断***Group变量的值是否包含要修改的新会签人,包含的话,无需add. 但需要更新相应的Size
                                if (((Set<String>) entry.getValue()).contains(newAccount)) {
                                    checkAndUpdateSize(process.getId(), variables, entry.getKey() + "Size");
                                } else {
                                    ((Set<String>) entry.getValue()).add(newAccount);
                                }
                                runtimeService.setVariable(process.getId(), entry.getKey(), entry.getValue());
                                logger.info("任务变更!!!原会签人:" + oldAccount + ",新会签人:" + newAccount + ";流程:" + process.getName() + "/" + process.getId() + "/" + entry.getKey());
                            }
                        } else if (entry.getValue() instanceof List) {
                            if (((List<String>) entry.getValue()).contains(oldAccount)) {
                                ((List<String>) entry.getValue()).remove(oldAccount);
                                //判断***Group变量的值是否包含要修改的新会签人,包含的话,无需add. 但需要更新相应的Size
                                if (((List<String>) entry.getValue()).contains(newAccount)) {
                                    checkAndUpdateSize(process.getId(), variables, entry.getKey() + "Size");
                                } else {
                                    ((List<String>) entry.getValue()).add(newAccount);
                                }
                                runtimeService.setVariable(process.getId(), entry.getKey(), entry.getValue());
                                logger.info("任务变更!!!原会签人:" + oldAccount + ",新会签人:" + newAccount + ";流程:" + process.getName() + "/" + process.getId() + "/" + entry.getKey());
                            }
                        } else if (entry.getValue() instanceof String) {
                            if (entry.getValue().toString().equals(oldAccount)) {
                                entry.setValue(newAccount);
                                runtimeService.setVariable(process.getId(), entry.getKey(), entry.getValue());
                                logger.info("任务变更!!!原会签人:" + oldAccount + ",新会签人:" + newAccount + ";流程:" + process.getName() + "/" + process.getId() + "/" + entry.getKey());
                            }
                        } else {
                            logger.info("无法处理的类型:" + entry.getValue());
                        }
                    }
                });
            });
        }
    }

    private void checkAndUpdateSize(String exId, Map<String, Object> variables, String sizeKey) {
        if (variables.containsKey(sizeKey)) {
            if (variables.get(sizeKey) instanceof Integer) {
                Integer integer = (Integer) variables.get(sizeKey);
                runtimeService.setVariable(exId, sizeKey, integer - 1);
            } else {
                logger.warn(sizeKey + ",字段类型不为int," + variables.get(sizeKey).getClass());
            }
        } else {
            logger.warn(sizeKey + "不存在对应的Size变量");
        }
    }

    public boolean hasParentProcess(String processDefinitionKey, String processId, String sysId) {
        if (StrUtil.equalsIgnoreCase(processDefinitionKey, FlowableConstant.AM_PROD_O32_NEW_FUND_MAINTENANCE_FLOW) && StrUtil.equalsIgnoreCase(sysId, FlowableConstant.AMPROD)) {
            HistoricProcessInstance hisPi = historyService.createHistoricProcessInstanceQuery().processInstanceTenantId(sysId).processInstanceId(processId).singleResult();
            return StrUtil.isNotBlank(hisPi.getSuperProcessInstanceId());
        }
        return false;
    }

    /**
     * 检索回退操作需要通知的用户列表
     *
     * @param processId  the process id
     * @param sourceList 要回退到的节点列表
     * @param targetList 要被回退到的节点列表
     * @return the set
     */
    public void callBackRemindActivitors(String processId, List<String> sourceList, List<String> targetList, Set<String> userSet, Set<String> taskSet) {
        logger.info("检索回退操作需要通知的用户列表,参数processId:" + processId);
        logger.info("检索回退操作需要通知的用户列表,参数sourceList:" + sourceList);
        logger.info("检索回退操作需要通知的用户列表,参数targetList:" + targetList);
        logger.info("流程id:{},要回退到节点:{}", processId, sourceList);
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        List<Comment> totalCommentList = taskService.getProcessInstanceComments(processId);
        //移除本次提交的批注
        logger.info("当前批注列表数量:" + totalCommentList.size());
        totalCommentList.remove(0);
        logger.info("移除最新条批注后列表数量:" + totalCommentList.size());
        List<TaskComment> collect = totalCommentList.stream().filter(e -> StrUtil.isNotBlank(e.getTaskId())).map(e -> {
            TaskComment tc = new TaskComment();
            tc.setTime(e.getTime());
            tc.setTaskId(e.getTaskId());
            tc.setUserId(e.getUserId());
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(e.getTaskId()).singleResult();
            tc.setTaskKey(historicTaskInstance.getTaskDefinitionKey());
            tc.setTaskName(historicTaskInstance.getName());
            return tc;
        }).collect(Collectors.toList());

        Map<String, List<String>> target2sourceMap = getNodeRelationMap(pi);
        for (TaskComment taskComment : collect) {
            if (sourceList.contains(taskComment.getTaskKey())) {
                userSet.add(taskComment.getUserId());
                taskSet.add(taskComment.getTaskName());
                sourceList.remove(taskComment.getTaskKey());
            } else {
                for (String source : sourceList) {
                    AtomicBoolean beforeEnd = new AtomicBoolean(true);
                    targetList.forEach(currentTaskKey -> {
                        int count = 0;
                        if (this.isBelongtoSourceByFlowMap(target2sourceMap.get(taskComment.getTaskKey()), currentTaskKey, target2sourceMap, count)) {
                            logger.info("currentTaskKey = " + currentTaskKey);
                            beforeEnd.set(false);
                        }
                    });
                    logger.info("beforeEnd:" + beforeEnd);
                    int count = 0;
                    if (beforeEnd.get() && this.isBelongtoSourceByFlowMap(target2sourceMap.get(taskComment.getTaskKey()), source, target2sourceMap, count)) {
                        userSet.add(taskComment.getUserId());
                        taskSet.add(taskComment.getTaskName());
                    }
                }
            }
            if (sourceList.isEmpty()) {
                break;
            }
        }
    }

    /**
     * 根据流程实例获取各节点->前置节点Map
     *
     * @param pi the process id
     * @return the Map
     */
    private Map<String, List<String>> getNodeRelationMap(ProcessInstance pi) {
        Map<String, List<String>> target2sourceMap = new HashMap<>();
        // 当前审批节点
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        Map<String, FlowElement> flowElementMap = bpmnModel.getMainProcess().getFlowElementMap();
        List<FlowElement> feList = flowElementMap.values().stream().filter(e -> e instanceof SequenceFlow).collect(Collectors.toList());
        for (FlowElement flowElement : feList) {
            String targetRef = ((SequenceFlow) flowElement).getTargetRef();
            String sourceRef = ((SequenceFlow) flowElement).getSourceRef();
            if (target2sourceMap.containsKey(targetRef)) {
                List<String> strings = target2sourceMap.get(targetRef);
                strings.add(sourceRef);
            } else {
                target2sourceMap.put(targetRef, CollectionUtil.newArrayList(sourceRef));
            }
        }
        return target2sourceMap;
    }

    private void getAllPostNode(Set<String> res, Map<String, List<String>> map, String task) {
        if (map.get(task) == null) {
            return;
        }
        res.addAll(map.get(task));
        map.get(task).stream().forEach(postTask -> this.getAllPostNode(res, map, postTask));
    }

    /**
     * 查询是否是目标节点的后续节点
     *
     * @param sourceActivityId 目标节点
     * @param sourceList       要检测的节点列表
     * @param target2sourceMap 各节点->前置节点Map
     * @return boolean
     */
    private boolean isBelongtoSourceByFlowMap(List<String> sourceList, String sourceActivityId, Map<String, List<String>> target2sourceMap, int count) {
        if (CollectionUtil.isEmpty(sourceList) || count > 200) {
            return false;
        }
        Set<String> set = new HashSet<>();
        for (int i = 0; i < sourceList.size(); i++) {
            if (sourceList.get(i).equals(sourceActivityId)) {
                return true;
            }
            if (sourceList.get(i).equals("startEvent") || sourceList.get(i).equals("subProcessStartEvent")) {
                continue;
            }
            set.addAll(target2sourceMap.get(sourceList.get(i)));
        }
        logger.info("isBelongtoSourceByFlowMap--sourceList:" + CollUtil.join(set, ","));
        count++;
        return isBelongtoSourceByFlowMap(new ArrayList<>(set), sourceActivityId, target2sourceMap, count);
    }

    public PageDataResult queryFullCommentsByProcessId(Integer pageNum, Integer pageSize, ParamCommentDto params) {
        try {
            String sql = "select a.handle_status as handle_status, b.ID_ as comment_id, a.task_id as task_id, a.task_name as task_name, a.process_id as process_id, " +
                    "COALESCE(b.FULL_MSG_, a.comment) as full_message, COALESCE(c.ASSIGNEE_, a.task_assignee) as user_id, COALESCE(b.TIME_, a.create_time) as time " +
                    "from (select process_id,task_id,task_name,handle_status,comment,task_assignee,create_time from t_flowable_comment_supplement " +
                    "where process_id = :processId and handle_status = 'SIGN') a left join ( select * from ACT_HI_COMMENT where PROC_INST_ID_ = :processId ) b on a.task_id = b.TASK_ID_ " +
                    "and b.TYPE_ = 'comment' left join (select * from ACT_HI_TASKINST where PROC_INST_ID_ = :processId ) c on a.task_id = c.ID_ order by time desc";
            Map<String, String> args = new HashMap<>();
            args.put("processId", params.getProcessId());
            List<FullComment> totalCommentList = namedParameterJdbcTemplateMysql.query(sql, args, new BeanPropertyRowMapper<>(FullComment.class));
            if (CollectionUtils.isEmpty(totalCommentList)) {
                return PageDataResult.empty();
            } else {
                List<ResponseCommentDto> resultList = new ArrayList<>();
                PageDataResult pageDataResult = new PageDataResult(pageNum, pageSize, totalCommentList);

                for (Object c : pageDataResult.getList()) {
                    FullComment co = (FullComment) c;
                    ResponseCommentDto dto = new ResponseCommentDto();
                    dto.setCommentId(co.getCommentId());
                    dto.setProcessInstanceId(co.getProcessId());
                    dto.setFullMessage(co.getFullMessage());
                    dto.setUserId(co.getUserId());
                    dto.setTaskId(co.getTaskId());
                    dto.setHandleStatus(co.getHandleStatus());

                    String taskId = co.getTaskId();
                    List<HistoricTaskInstance> tasklist = historyService.createHistoricTaskInstanceQuery().taskId(taskId).list();
                    if (CollUtil.isEmpty(tasklist)) {//强制结束,会设置一个假的taskid
                        List<FlowableCommentSupplement> taskSuppList = flowableCommentSupplementService.findByTaskId(taskId);
                        dto.setTaskName(taskSuppList.get(0).getTaskName());
                    } else {
                        dto.setTaskDefinitionKey(tasklist.get(0).getTaskDefinitionKey());
                        dto.setTaskName(tasklist.get(0).getName());
                    }
                    dto.setUserName(flowableEhrEmpService.getChineseName(co.getUserId()));
                    dto.setTime(convertTime(co.getTime()));
                    resultList.add(dto);
                }
                pageDataResult.setList(resultList);
                return pageDataResult;
            }
        } catch (Exception e) {
            logger.error("分页查询评论错误！", e);
            throw new RuntimeException("DemandManagementConfirmHandler处理异常，结束处理。processid=", e);
        }
    }

    public FlowableResponse createForward(ForwardParamDto params) {
        try {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(params.getExProcessId()).singleResult();
            if (historicProcessInstance == null) {
                throw new RuntimeException("待转发流程不存在!");
            }
            HashMap<String, Object> map = new HashMap<>();
            map.put("userid", params.getUserId());
            map.put("username", flowableEhrEmpService.getChineseName(params.getUserId()));
            map.put("exProcessId", params.getExProcessId());
            map.put("exTaskId", params.getExTaskId());
            map.put("forwardid", params.getForwardId());
            map.put("forwardname", flowableEhrEmpService.getChineseName(params.getForwardId()));
            map.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true);
            Authentication.setAuthenticatedUserId(params.getUserId());
            String businesskey = FlowableConstant.AM_PROD_FORWARD_FLOW + "." + params.getTitle();

            ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(FlowableConstant.AM_PROD_FORWARD_FLOW, businesskey, map, params.getSysId());
            logger.info("create forward:-----" + processInstance.getProcessDefinitionId());
            runtimeService.setVariable(processInstance.getProcessInstanceId(), "_FLOWABLE_SKIP_EXPRESSION_ENABLED", true);
            runtimeService.setProcessInstanceName(processInstance.getProcessInstanceId(), params.getTitle());
            //信息入库
            flowableDetailService.addProcessInstanceByCreate(processInstance, params.getSysId(), params.getComment(), params.getUserId(), params.getTitle());
            //begintask自动通过
            List<Task> tasks = taskService.createTaskQuery().taskAssignee(params.getUserId()).orderByTaskCreateTime().desc().list();
            String taskId = "";
            Task task = null;
            for (Task t : tasks) {
                if (t.getProcessInstanceId().equals(processInstance.getProcessInstanceId())) {
                    taskId = t.getId();
                    task = t;
                    break;
                }
            }
            taskService.addComment(taskId, task.getProcessInstanceId(), params.getComment());
            taskService.complete(taskId);
            //信息入库
            flowableDetailService.addProcessInstanceByTask(task, processInstance, params.getComment(), JSONUtil.toJsonStr(params), params.getTitle(), "createForward");
            return FlowableResponse.success(new ResponseCreateDto(processInstance.getProcessInstanceId()));
        } catch (Exception e) {
            logger.error("创建转发流程失败，请检查！", e);
            return FlowableResponse.error("创建转发流程失败，请检查！e.getMessage=" + e.getMessage());
        }
    }

    /**
     * 存漏洞, 流程定义变化会导致查询结果不准. 需要遍历每个流程去做判断.待明确需求再做开发
     */
    public List<String> queryNotArrivedProcessList(ParamSearchArrivedDto paramSearchDto) {
        if (CollUtil.isEmpty(paramSearchDto.getProcessIdList()) || StrUtil.isBlank(paramSearchDto.getTaskDefineKey())) {
            return Collections.emptyList();
        }
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(paramSearchDto.getProcessIdList().get(0)).singleResult();
        Map<String, List<String>> target2sourceMap = getNodeRelationMap(pi);
        Set<String> taskSet = new HashSet<>();
        this.getAllPostNode(taskSet, target2sourceMap, paramSearchDto.getTaskDefineKey());
        if (BooleanUtil.isTrue(paramSearchDto.getContainsCurrTask())) {
            taskSet.add(paramSearchDto.getTaskDefineKey());
        }
        String sql = "SELECT distinct PROC_INST_ID_ FROM ACT_RU_TASK WHERE PROC_INST_ID_ IN (:processIdList) AND TASK_DEF_KEY_ IN (:taskDefineKeyList)";
        Map<String, Object> args = new HashMap<>();
        args.put("processIdList", paramSearchDto.getProcessIdList());
        args.put("taskDefineKeyList", taskSet);
        List<String> processIdList = namedParameterJdbcTemplateMysql.queryForList(sql, args, String.class);
        return processIdList;
    }


    public List<String> queryDoingNodeProcessList(ParamSearchArrivedDto paramSearchDto) {
        if (CollUtil.isEmpty(paramSearchDto.getProcessIdList()) || StrUtil.isBlank(paramSearchDto.getTaskDefineKey())) {
            return Collections.emptyList();
        }
        String sql = "SELECT distinct PROC_INST_ID_ FROM ACT_RU_TASK WHERE PROC_INST_ID_ IN (:processIdList) AND TASK_DEF_KEY_ =:taskDefineKey";
        Map<String, Object> args = new HashMap<>();
        args.put("processIdList", paramSearchDto.getProcessIdList());
        args.put("taskDefineKey", paramSearchDto.getTaskDefineKey());
        List<String> processIdList = namedParameterJdbcTemplateMysql.queryForList(sql, args, String.class);
        return processIdList;
    }

    public PageDataResult<ResponseSituationDto> searchProcessList(Pageable pageable, ParamSearchDto params) {
        List<ResponseSituationDto> list = new ArrayList<>();
        String title = params.getTitle();
        String startUser = params.getStartUser();
        String sysid = params.getSysid();
        String endStr = params.getHasProcessEnd();
        List<String> processDefineKeyList = StrUtil.isNotEmpty(params.getProcessDefineKey()) ? StrUtil.split(params.getProcessDefineKey(), ',') : new ArrayList<>();
        Date begin = StrUtil.isNotBlank(params.getBeginDate()) ? DateUtil.parse(params.getBeginDate(), "yyyyMMdd").toJdkDate() : null;
        Date end = StrUtil.isNotBlank(params.getEndDate()) ? DateUtil.offsetDay(DateUtil.parse(params.getEndDate(), "yyyyMMdd"), 1).toJdkDate() : null;
        long totalRowCnt = 0;
        int pageRowStartNo = ((Long) pageable.getOffset()).intValue();
        List<HistoricProcessInstance> hpis = null;
        title = StrUtil.isEmpty(title) ? null : title;
        sysid = StrUtil.isEmpty(sysid) ? null : sysid;
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery().startedBefore(end).startedAfter(begin);
        if (StrUtil.isNotEmpty(endStr)) {
            if (StrUtil.equals(endStr, "1")) {
                historicProcessInstanceQuery.finished();
            } else if (StrUtil.equals(endStr, "0")) {
                historicProcessInstanceQuery.unfinished();
            }
        }
        if (StrUtil.isNotEmpty(sysid)) {
            historicProcessInstanceQuery.processInstanceTenantId(sysid);
        }
        if (StrUtil.isNotEmpty(title)) {
            historicProcessInstanceQuery.processInstanceNameLike("%" + title + "%");
        }
        if (StrUtil.isNotBlank(startUser)) {
            historicProcessInstanceQuery.startedBy(startUser);
        } else {
            processDefineKeyList.remove(FlowableConstant.AM_PROD_FORWARD_FLOW);
        }
        if (CollectionUtil.isNotEmpty(processDefineKeyList)) {
            historicProcessInstanceQuery.processDefinitionKeyIn(processDefineKeyList);
        }
        List<String> processIdList = params.getProcessIdList();
        if (Objects.nonNull(params.getArchived()) && params.getArchived()) {
            List<FlowableDetail> details = flowableDetailService.findAllByProcessIdIn(processIdList, "2");
            processIdList = details.stream().map(FlowableDetail::getProcessInstanceId).collect(Collectors.toList());
            historicProcessInstanceQuery.processInstanceIds(new HashSet<>(processIdList));
            hpis = CollectionUtil.isEmpty(processIdList) ? Collections.EMPTY_LIST : historicProcessInstanceQuery.orderByProcessInstanceStartTime().desc().listPage(pageRowStartNo, pageable.getPageSize());
            totalRowCnt = CollectionUtil.isEmpty(processIdList) ? 0 : historicProcessInstanceQuery.count();
        } else {
            if (CollectionUtil.isNotEmpty(processIdList)) {
                historicProcessInstanceQuery.processInstanceIds(new HashSet<>(processIdList));
            }
            hpis = historicProcessInstanceQuery.orderByProcessInstanceStartTime().desc().listPage(pageRowStartNo, pageable.getPageSize());
            totalRowCnt = historicProcessInstanceQuery.count();
        }
        buildResSituationDto(sysid, list, hpis);
        PageDataResult<ResponseSituationDto> pageDataResult = new PageDataResult<>();
        pageDataResult.setList(list);
        pageDataResult.setTotals((int) totalRowCnt);
        return pageDataResult;
    }

    private void buildResSituationDto(String sysid, List<ResponseSituationDto> list, List<HistoricProcessInstance> hpis) {
        if (CollectionUtils.isEmpty(hpis)) {
            return;
        }
        List<String> pidList = hpis.stream().map(HistoricProcessInstance::getId).collect(Collectors.toList());
        TaskQuery taskQuery = taskService.createTaskQuery();
        taskQuery.processInstanceIdIn(pidList);
        if (StrUtil.isNotEmpty(sysid)) {
            taskQuery.taskTenantId(sysid);
        }
        List<Task> taskFullList = taskQuery.orderByTaskCreateTime().desc().list();
        Map<String, List<Task>> processTaskMap = taskFullList.stream().collect(Collectors.groupingBy(Task::getProcessInstanceId));

        for (HistoricProcessInstance pi : hpis) {
            ResponseSituationDto psd = new ResponseSituationDto();
            psd.setStatus("0");
            List<CurrentTaskInfoDto> currentTaskInfoDtoList = new ArrayList<>();
            //如果流程没结束，查询到任务节点
            if (ObjectUtil.isNull(pi.getEndTime())) {
                List<Task> taskList = processTaskMap.get(pi.getId());
                if (CollectionUtil.isNotEmpty(taskList)) {
                    Map<String, String> taskStatusMap = getTaskHandleMapByTaskList(taskList);
                    Map<String, List<String>> assigneemap = new HashMap<>();
                    Map<String, List<String>> taskidmap = new HashMap<>();
                    Map<String, List<String>> datemap = new HashMap<>();
                    Map<String, List<String>> statusmap = new HashMap<>();
                    List<String> sbkeyList = new ArrayList<>();
                    List<String> sbnameList = new ArrayList<>();
                    List<String> sbidList = new ArrayList<>();
                    List<String> startTimeList = new ArrayList<>();
                    List<String> sbassigneeList = new ArrayList<>();
                    List<String> sbStatusList = new ArrayList<>();
                    taskList.forEach(task -> {
                        sbkeyList.add(task.getTaskDefinitionKey());
                        sbnameList.add(task.getName());
                        sbidList.add(task.getId());
                        startTimeList.add(DateUtil.format(task.getCreateTime(), "yyyy-MM-dd hh:mm:ss"));
                        sbassigneeList.add(task.getAssignee());
                        sbStatusList.add(taskStatusMap.get(task.getId()));

                        String key = task.getTaskDefinitionKey() + "|" + task.getName();
                        List<String> assigneeList = new ArrayList<>();
                        List<String> taskIdList = new ArrayList<>();
                        List<String> dateList = new ArrayList<>();
                        List<String> statusList = new ArrayList<>();

                        if (assigneemap.get(key) != null) {
                            assigneeList = assigneemap.get(key);
                        }
                        assigneeList.add(task.getAssignee());
                        assigneemap.put(key, assigneeList);

                        if (taskidmap.get(key) != null) {
                            taskIdList = taskidmap.get(key);
                        }
                        taskIdList.add(task.getId());
                        taskidmap.put(key, taskIdList);

                        if (datemap.get(key) != null) {
                            dateList = datemap.get(key);
                        }
                        dateList.add(convertTime(task.getCreateTime()));
                        datemap.put(key, dateList);

                        if (statusmap.get(key) != null) {
                            statusList = statusmap.get(key);
                        }
                        statusList.add(taskStatusMap.get(task.getId()));
                        statusmap.put(key, statusList);
                    });

                    psd.setCurrentTaskDefinitionKey(CollectionUtil.join(sbkeyList, "|"));
                    psd.setCurrentTaskName(CollectionUtil.join(sbnameList, "|"));
                    psd.setCurrentTaskId(CollectionUtil.join(sbidList, "|"));
                    psd.setCurrentAssignee(CollectionUtil.join(sbassigneeList, "|"));
                    psd.setCurrentTaskStartTime(CollectionUtil.join(startTimeList, "|"));
                    psd.setCurrentAssigneeName(flowableEhrEmpService.getChineseNameAll(sbassigneeList));
                    psd.setCurrentTaskHandleStatus(CollectionUtil.join(sbStatusList, "|"));

                    for (Map.Entry<String, List<String>> m : assigneemap.entrySet()) {
                        List<String> userid = m.getValue();
                        List<String> username = new ArrayList<>();
                        for (String id : userid) {
                            username.add(flowableEhrEmpService.getChineseName(id));
                        }
                        String[] defName = m.getKey().split("\\|");
                        CurrentTaskInfoDto currentTaskInfoDto = new CurrentTaskInfoDto();
                        currentTaskInfoDto.setCurrentTaskName(defName[1]);
                        currentTaskInfoDto.setCurrentTaskDefinitionKey(defName[0]);
                        currentTaskInfoDto.setCurrentAssigneeList(m.getValue());
                        currentTaskInfoDto.setCurrentAssigneeNameList(username);
                        currentTaskInfoDto.setCurrentTaskIdList(taskidmap.get(m.getKey()));
                        currentTaskInfoDto.setCurrentTaskCreateTimeList(datemap.get(m.getKey()));
                        currentTaskInfoDto.setCurrentTaskStatusList(statusmap.get(m.getKey()));
                        currentTaskInfoDtoList.add(currentTaskInfoDto);
                    }
                }
            } else {
                psd.setStatus("1");
                List<FlowableDetail> delist = flowableDetailService.findDetailByProcessId(pi.getId());
                if (CollUtil.isNotEmpty(delist)) {
                    String status = delist.get(0).getProcessStatus();
                    //status2为stop手动结束，置状态
                    //status3为delete，不会出现在这个接口
                    psd.setStatus(StrUtil.equals(delist.get(0).getProcessStatus(), "2") ? "2" : delist.get(0).getProcessStatus());
                }
                psd.setEndTime(convertTime(pi.getEndTime()));
            }
            psd.setProcessTitle(pi.getName());
            psd.setProcessId(pi.getId());
            psd.setProcessDefineName(pi.getProcessDefinitionName());
            psd.setProcessDefineKey(pi.getProcessDefinitionKey());
            if (StrUtil.isBlank(pi.getProcessDefinitionKey())) {
                logger.info("记录: pi查询不到流程定义" + pi.getId());
                ProcessInstance p = runtimeService.createProcessInstanceQuery().processInstanceId(pi.getId()).singleResult();
                if (p == null) {//pi.getProcessDefinitionKey() 历史数据中有可能不存在
                    logger.info("记录: pi在runtimeService查询不到流程定义" + pi.getId());
                    HistoricProcessInstance hp = historyService.createHistoricProcessInstanceQuery().processInstanceId(pi.getId()).singleResult();
                    if (hp != null) {
                        psd.setProcessDefineKey(hp.getProcessDefinitionKey());
                    }
                    logger.info("记录: pi在historyService查询不到流程定义" + pi.getId());
                } else {
                    psd.setProcessDefineKey(p.getProcessDefinitionKey());
                }
            }

            psd.setStartTime(convertTime(pi.getStartTime()));
            psd.setStartUsername(pi.getStartUserId());
            psd.setStartUserChineseName(flowableEhrEmpService.getChineseName(pi.getStartUserId()));
            psd.setSysid(pi.getTenantId());
            psd.setCurrentTaskInfoDtoList(currentTaskInfoDtoList);
            if (CollUtil.isNotEmpty(currentTaskInfoDtoList)) {
                List<String> collect = currentTaskInfoDtoList.stream().map(dto -> dto.getCurrentTaskName() + "(" + CollUtil.join(dto.getCurrentAssigneeNameList(), ",") + ")").collect(Collectors.toList());
                psd.setCurrentTaskSummaryInfo(CollUtil.join(collect, ","));
            }
            //已办任务涉及
            if (StrUtil.equals(psd.getProcessDefineKey(), FlowableConstant.AM_PROD_FORWARD_FLOW)) {
                HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(((HistoricProcessInstanceEntityImpl) pi).getProcessInstanceId()).list().stream().filter(e -> e.getVariableName().equals("exProcessId")).findFirst().get();
                HistoricProcessInstance expi = historyService.createHistoricProcessInstanceQuery().processInstanceId(StrUtil.toString(variableInstance.getValue())).singleResult();
                if (expi != null) {
                    psd.setProcessId(expi.getId());
                    psd.setProcessDefineKey(expi.getProcessDefinitionKey());
                }
            }
            list.add(psd);
        }
    }

    public PageDataResult<ResponseSituationDto> searchDealProcessList(Pageable pageable, ParamSearchDto params) {
        List<ResponseSituationDto> list = new ArrayList<>();
        String title = params.getTitle();
        List<String> dealUser = Strings.isNotBlank(params.getDealUser()) ? CollUtil.newArrayList(params.getDealUser()) : new ArrayList<>();
        if (CollectionUtil.isNotEmpty(params.getDealUserList())) {
            dealUser.addAll(new HashSet<>(params.getDealUserList()));
        }
        String processDefineKeyOrig = params.getProcessDefineKey();
        String sysid = params.getSysid();
        List<String> processDefineKeyList = StrUtil.isNotEmpty(processDefineKeyOrig) ? StrUtil.split(processDefineKeyOrig, ',') : new ArrayList<>();
        if (CollectionUtil.isNotEmpty(params.getProcessDefineKeyList())) {
            processDefineKeyList.addAll(new HashSet<>(params.getProcessDefineKeyList()));
        }
        long totalRowCnt;
        int pageRowStartNo = ((Long) pageable.getOffset()).intValue();
        List<Task> taskPageList;
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (StrUtil.isNotEmpty(sysid)) {
            taskQuery.taskTenantId(sysid);
        }
        if (StrUtil.isNotEmpty(title)) {
            taskQuery.processInstanceBusinessKeyLike("%" + title + "%");
        }
        if (CollectionUtil.isNotEmpty(processDefineKeyList)) {
            taskQuery.processDefinitionKeyIn(processDefineKeyList);
        }
        if (CollectionUtil.isNotEmpty(dealUser)) {
            taskQuery.taskAssigneeIds(dealUser);
        }
        List<String> processIdList = params.getProcessIdList();
        if (CollectionUtil.isNotEmpty(processIdList)) {
            taskQuery.processInstanceIdIn(processIdList);
        }
        if (Objects.nonNull(params.getArchived()) && params.getArchived()) {
            List<FlowableDetail> details = flowableDetailService.findAllByProcessIdIn(processIdList, "2");
            processIdList = details.stream().map(FlowableDetail::getProcessInstanceId).collect(Collectors.toList());
            taskQuery.processInstanceIdIn(processIdList);
            taskPageList = CollectionUtil.isEmpty(processIdList) ? Collections.EMPTY_LIST : taskQuery.orderBy(TaskQueryProperty.CREATE_TIME).desc().listPage(pageRowStartNo, pageable.getPageSize());
            totalRowCnt = CollectionUtil.isEmpty(processIdList) ? 0 : taskQuery.count();
        } else {
            taskPageList = taskQuery.orderBy(TaskQueryProperty.CREATE_TIME).desc().listPage(pageRowStartNo, pageable.getPageSize());
            totalRowCnt = taskQuery.count();
        }
        Map<String, String> taskStatusMap = getTaskHandleMapByTaskList(taskPageList);

        for (Task task : taskPageList) {
            String processId = task.getProcessInstanceId();
            if (processId == null) {
                //说明是子任务，没有processid
                Task parentTask = taskService.createTaskQuery().taskId(task.getParentTaskId()).singleResult();
                processId = parentTask.getProcessInstanceId();
            }
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            ResponseSituationDto psd = new ResponseSituationDto();
            psd.setCurrentTaskId(task.getId());
            psd.setCurrentTaskName(task.getName());
            psd.setCurrentTaskDefinitionKey(task.getTaskDefinitionKey());
            psd.setProcessTitle(pi.getName());
            psd.setProcessId(pi.getId());
            psd.setProcessDefineName(pi.getProcessDefinitionName());
            psd.setProcessDefineKey(pi.getProcessDefinitionKey());
            psd.setStartTime(convertTime(pi.getStartTime()));
            psd.setStartUsername(pi.getStartUserId());
            psd.setStartUserChineseName(flowableEhrEmpService.getChineseName(pi.getStartUserId()));
            psd.setCurrentAssignee(task.getAssignee());
            psd.setCurrentAssigneeName(flowableEhrEmpService.getChineseName(task.getAssignee()));
            psd.setSysid(pi.getTenantId());
            psd.setStatus("0");
            psd.setCurrentTaskHandleStatus(taskStatusMap.get(task.getId()));

            if (StrUtil.equals(pi.getProcessDefinitionKey(), FlowableConstant.AM_PROD_FORWARD_FLOW)) {
                Object exProcessId = historyService.createHistoricVariableInstanceQuery().processInstanceId(pi.getProcessInstanceId()).variableName("exProcessId").singleResult().getValue();
                HistoricProcessInstance expi = historyService.createHistoricProcessInstanceQuery().processInstanceId(StrUtil.toString(exProcessId)).singleResult();
                if (expi != null) {
                    psd.setProcessId(expi.getId());
                    psd.setProcessDefineKey(expi.getProcessDefinitionKey());
                }
            }
            list.add(psd);
        }
        PageDataResult<ResponseSituationDto> pageDataResult = new PageDataResult<>();
        pageDataResult.setList(list);
        pageDataResult.setTotals((int) totalRowCnt);
        return pageDataResult;
    }

    public List<String> queryProcessListByStartUser(ParamStartPorcessDto paramStartPorcessDto) {
        StringBuilder sqlBuilder = new StringBuilder("select distinct PROC_INST_ID_ from (select a.PROC_INST_ID_, a.START_TIME_ from ACT_HI_PROCINST a inner join ACT_RE_PROCDEF b on a.PROC_DEF_ID_ = b.ID_ where a.TENANT_ID_ =:sysId ");
        if (CollUtil.isNotEmpty(paramStartPorcessDto.getAccountList())) {
            sqlBuilder.append(" and a.START_USER_ID_ in (:accList) ");
        }
        if (CollUtil.isNotEmpty(paramStartPorcessDto.getProcessDefineKeyList())) {
            sqlBuilder.append(" and b.KEY_ in (:processDefineKeyList) ");
        }
        if (StrUtil.isNotBlank(paramStartPorcessDto.getStartBeginTime())) {
            sqlBuilder.append(" and a.START_TIME_>=:startBeginTime ");
        }
        if (StrUtil.isNotBlank(paramStartPorcessDto.getStartAfterTime())) {
            sqlBuilder.append(" and a.START_TIME_<=:startAfterTime ");
        }
        sqlBuilder.append(" order by a.START_TIME_ desc ) tmp");
        Map<String, Object> args = new HashMap<>();
        args.put("accList", paramStartPorcessDto.getAccountList());
        args.put("sysId", paramStartPorcessDto.getSysId());
        args.put("processDefineKeyList", paramStartPorcessDto.getProcessDefineKeyList());
        args.put("startBeginTime", paramStartPorcessDto.getStartBeginTime());
        args.put("startAfterTime", paramStartPorcessDto.getStartAfterTime());
        return namedParameterJdbcTemplateMysql.queryForList(sqlBuilder.toString(), args, String.class);
    }

    public List<String> queryProcessListByCurrentAssignee(String dealUser, String processDefineKey, String createProject) {
        StringBuilder sqlBuilder = new StringBuilder("select distinct PROC_INST_ID_ from ACT_RU_TASK a where a.TENANT_ID_ =:createProject ");
        sqlBuilder.append(" and a.PROC_DEF_ID_ LIKE CONCAT(:processDefineKey,'%') ");
        sqlBuilder.append(" and a.ASSIGNEE_ = :dealUser ");
//        sqlBuilder.append(" order by a.CREATE_TIME_ desc ");
        Map<String, Object> args = new HashMap<>();
        args.put("createProject", createProject);
        args.put("processDefineKey", processDefineKey);
        args.put("dealUser", dealUser);
        return namedParameterJdbcTemplateMysql.queryForList(sqlBuilder.toString(), args, String.class);
    }

    public List<String> getCopyUserList(ParticipateParamDto params) {
        if(CollUtil.isEmpty(params.getProcessIdList())){
            return new ArrayList<>();
        }
       return flowableCopyService.getCopyUserList(params.getProcessIdList());
    }

    public void clearCache(String name) {
        Cache cache = cacheManager.getCache(name);
        if(cache !=null){
            cache.clear();
        }
    }
}
